﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System;
using System.Reflection;
using UnityEditorInternal;
using UnityEngine.Events;
using System.Text;
using System.Linq;

namespace Titan.UI
{
    [CustomPropertyDrawer(typeof(CallEvent))]
    public class CallEventDrawer : PropertyDrawer
    {
        private struct ValidMethodMap
        {
            public UnityEngine.Object target;
            public MethodInfo methodInfo;
            public PersistentListenerMode mode;
        }

        private int m_index = 0;

        #region Override
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            EditorGUILayout.LabelField("Call:");
            EditorGUILayout.Space();
            EditorGUILayout.BeginHorizontal();
            EditorGUI.BeginChangeCheck();

            property.FindPropertyRelative("Target").objectReferenceValue = EditorGUILayout.ObjectField(
                property.FindPropertyRelative("Target").objectReferenceValue, typeof(UnityEngine.Object),true);
            

            List<ValidMethodMap> list = new List<ValidMethodMap>();

            GetMethodsForTargetAndMode(property.FindPropertyRelative("Target").objectReferenceValue, new Type[]
			{
                typeof(EventArgs)
			}, list, PersistentListenerMode.Object);

            List<string> nameList = new List<string>();
            for (int i = 0; i < list.Count;++i )
            {
                nameList.Add(list[i].methodInfo.Name);
            }
            string curMethodName = property.FindPropertyRelative("MethodName").stringValue;
            if (curMethodName != null && curMethodName.Length > 0)
            {
                for (int i = 0; i < nameList.Count; ++i)
                {
                    if (nameList[i] == curMethodName)
                    {
                        m_index = i;
                    }
                }
            }

            m_index = EditorGUILayout.Popup(m_index, nameList.ToArray());

            if (EditorGUI.EndChangeCheck())
            {
                if (nameList.Count > m_index)
                {
                    property.FindPropertyRelative("MethodName").stringValue = nameList[m_index];
                    property.FindPropertyRelative("Script").objectReferenceValue = list[m_index].target;
                    ParameterInfo[] parameters = list[m_index].methodInfo.GetParameters();
                    property.FindPropertyRelative("ParamType").stringValue = parameters[0].ParameterType.ToString();
                }
            }
            
            property.FindPropertyRelative("ParamObj").objectReferenceValue = EditorGUILayout.ObjectField(
                property.FindPropertyRelative("ParamObj").objectReferenceValue, typeof(UnityEngine.Object),true);

            EditorGUILayout.EndHorizontal();
        }
        #endregion

        #region Private Method

        #endregion

        #region Inner
        private void GetMethodsForTargetAndMode(UnityEngine.Object target, Type[] delegateArgumentsTypes, List<ValidMethodMap> methods, PersistentListenerMode mode)
        {
            if (target == null)
            {
                return;
            }
            MonoBehaviour[] mb = ((GameObject)target).GetComponents<MonoBehaviour>();
            foreach (var each in mb)
            {
                IEnumerable<ValidMethodMap> enumerable = CalculateMethodMap(each, delegateArgumentsTypes, mode == PersistentListenerMode.Object);
                foreach (ValidMethodMap current in enumerable)
                {

                    ValidMethodMap item = current;
                    item.mode = mode;
                    methods.Add(item);
                }
            }
        }


        private IEnumerable<ValidMethodMap> CalculateMethodMap(UnityEngine.Object target, Type[] t, bool allowSubclasses)
        {
            List<ValidMethodMap> list = new List<ValidMethodMap>();
            if (target == null || t == null)
            {
                return list;
            }
            Type type = target.GetType();
            List<MethodInfo> list2 = (
                from x in type.GetMethods()
                where !x.IsSpecialName
                select x).ToList<MethodInfo>();
            IEnumerable<PropertyInfo> source = type.GetProperties().AsEnumerable<PropertyInfo>();
            source =
                from x in source
                where x.GetCustomAttributes(typeof(ObsoleteAttribute), true).Length == 0 && x.GetSetMethod() != null
                select x;
            list2.AddRange(
                from x in source
                select x.GetSetMethod());
            foreach (MethodInfo current in list2)
            {
                ParameterInfo[] parameters = current.GetParameters();
                if (parameters.Length != 2 || parameters[1].ParameterType !=  typeof(UnityEngine.Object))
                {
                    continue;
                }

                ParameterInfo returnParam = current.ReturnParameter;
                if(returnParam != null && returnParam.ParameterType != typeof(void))
                {
                    continue;
                }

                ParameterInfo param1 = parameters[0];
                for (int i = 0; i < t.Length; ++i)
                {
                    if (param1.ParameterType.IsSubclassOf(t[i]))
                    {
                        list.Add(new ValidMethodMap
                        {
                            target = target,
                            methodInfo = current
                        });
                    }
                }


            }
            return list;
        }
        #endregion

    }
}
